/*
**  Copyright (c) 2014 Michael Liu(michael.liu.point@gmail.com).
**
**  Project: Gateway Unified Management Platform
**  File:    sadev.c
**  Author:  Michael
**  Date:    07/09/2014
**
**  Purpose:
**    system devices management implement.
*/

/* Include files. */

#include "common.h"
#include "dpmgr.h"
#include "eamgr.h"
#include "samgr.h"
#include "osdep.h"
#include "pfdep.h"

/* Macro constant definitions. */

/* Type definitions. */

/* Local function declarations. */

static e_ret sa_dev_method_add_ref(tsa_dev * sa_dev, void *ref);
static e_ret sa_dev_method_del_ref(tsa_dev * sa_dev, void *ref);

/* Macro API definitions. */

/* Global variable declarations. */

e_ret sa_dev_prepare(tsa_dev * sa_dev, e_ret(*opt) (tsa_dev * sa_dev, unsigned char opt, void *cfg))
{

	e_ret ret;
	tsa_dev *based_on_dev;

	sysmgr_trace_enter();

	ASSERT(sa_dev != NULL);

	if (sa_dev->prepare == eBOOL_TRUE) {
		sysmgr_dbg("system admin device [%s] already prepared.\n", sa_dev->name);
		sysmgr_trace_exit();
		return (eRET_SUCCESS);
	}

	sysmgr_dbg("system admin device [%s] prepare.\n", sa_dev->name);

	sa_dev->refs_cnt = 0;
	sa_dev->add_ref = sa_dev_method_add_ref;
	sa_dev->del_ref = sa_dev_method_del_ref;
	sa_dev->opt = opt;
	sa_dev->prepare = eBOOL_TRUE;

	based_on_dev = sa_dev->based_on_dev;
	if ((based_on_dev != NULL) && (based_on_dev->prepare)) {
		if (based_on_dev->add_ref != NULL) {
			ret = based_on_dev->add_ref(based_on_dev, sa_dev);
			if (ret != eRET_SUCCESS) {
				sysmgr_err("based on dev[%d] add_ref(%s) failed, ret: %d\n",
					   based_on_dev->name, sa_dev->name, ret);
			}
		}
	}

	sysmgr_trace_exit();
	return (eRET_SUCCESS);
}

e_ret sa_dev_method_add_ref(tsa_dev * sa_dev, void *ref)
{
	int i;
	e_ret ret;

	sysmgr_trace_enter();

	if (sa_dev->refs_cnt == 0) {
		sysmgr_dbg("sa dev[%s] first reference[%s] added, do init\n", sa_dev->name, ((tsa_dev *) ref)->name);
		if (sa_dev->opt != NULL) {
			sa_dev->opt(sa_dev, eSA_DEV_OPT_INIT, NULL);
			sa_dev->refs_cnt = 1;
			sa_dev->refs[0] = ref;
		}
		ret = eRET_SUCCESS;
	} else {
		sysmgr_dbg("sa dev[%s] no.%d reference[%s] added.\n",
			   sa_dev->name, sa_dev->refs_cnt + 1, ((tsa_dev *) ref)->name);
		for (i = 0; i < DP_DEV_REF_IF_MAX; i++) {
			if (sa_dev->refs[i] == NULL) {
				sa_dev->refs[i] = ref;
				sa_dev->refs_cnt++;
				break;
			}
		}
		if (i >= DP_DEV_REF_IF_MAX) {
			ret = eRET_NO_RESOURCE;
		} else {
			ret = eRET_SUCCESS;
		}
	}

	sysmgr_trace_exit();
	return (ret);
}

e_ret sa_dev_method_del_ref(tsa_dev * sa_dev, void *ref)
{
	int i;
	e_ret ret;

	sysmgr_trace_enter();

	for (i = 0; i < DP_DEV_REF_IF_MAX; i++) {
		if (sa_dev->refs[i] == ref) {
			sa_dev->refs[i] = NULL;
			sa_dev->refs_cnt--;
			break;
		}
	}

	if (i >= DP_DEV_REF_IF_MAX) {
		ret = eRET_NOT_EXIST;
	} else {
		if (sa_dev->refs_cnt == 0) {
			sysmgr_dbg("sa dev[%s] last reference[%s] removed, do clean\n",
				   sa_dev->name, ((tsa_dev *) ref)->name);
			if (sa_dev->opt != NULL) {
				sa_dev->opt(sa_dev, eSA_DEV_OPT_CLEAN, NULL);
			}
		} else {
			sysmgr_dbg("sa dev[%s] no.%d reference[%s] removed.\n",
				   sa_dev->name, sa_dev->refs_cnt, ((tsa_dev *) ref)->name);
		}
		ret = eRET_SUCCESS;
	}

	sysmgr_trace_exit();
	return (ret);
}
